/*
 * graph2D
 * Copyright (c) 2009 Shun Moriya <shun@mnu.sakura.ne.jp>
 *
 * The MIT License
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#import "desktopView.h"
#import "text_impl.h"
#import "../../desktop.h"
#import "../../profile.h"

@interface DesktopView(Private)
+(Class)layerClass;
-(BOOL)createFramebuffer;
-(void)destroyFramebuffer;
-(void)initializeAnimation;
-(void)finalizeAnimation;
#if defined(GRAPH2D_DESKTOP_VIEW_USE_DISPLAY_LINK)
-(void)drawView:(CADisplayLink*)displayLink;
#else
-(void)drawView;
#endif
@end

@implementation DesktopView

@synthesize context;
@synthesize animationTimer;
@synthesize animationInterval;

-(id)initWithFrame:(CGRect)aRect scale:(float)contentScaleFactor screen:(CGSize)screen
{
	if(self = [super initWithFrame:aRect])
	{
		Graph2D::Desktop::initialize();

		// 仮想スクリーンサイズを保存
		screenSize = screen;

		// 描画レイヤーを設定
		CAEAGLLayer* eaglLayer = (CAEAGLLayer*)self.layer;
		eaglLayer.opaque = YES;
		eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
										[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking,
										kEAGLColorFormatRGBA8/*kEAGLColorFormatRGB565*/, kEAGLDrawablePropertyColorFormat,
										nil];

		// 描画コンテキストを生成
		context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
		if(!context || ![EAGLContext setCurrentContext:context])
		{
		    [self release];
		    return nil;
		}

		// スケールファクタを設定
		self.contentScaleFactor = contentScaleFactor;

#if defined(GRAPH2D_DESKTOP_VIEW_USE_DISPLAY_LINK)
		[self drawView:nil];
		m_timestamp = CACurrentMediaTime();

		// アニメーションインターバルを設定
		[self setAnimationInterval:2];
#else
		// アニメーションインターバルを設定
		[self setAnimationInterval:2.0f / 60.0f];
#endif

		GRAPH2D_CHECK_GL_ERROR();

		// リソースを初期化
		Graph2D::Desktop::getInstance().initializeResources();

		// テキストキャッシュを初期化
		Graph2D::Implementation::Text::Cache::initialize(100);
	}

	return self;
}

-(void)dealloc
{
	// テキストキャッシュを解放
	Graph2D::Implementation::Text::Cache::finalize();
	// アニメーションを停止
	[self stopAnimation];
	// フレームバッファーを開放
	[self destroyFramebuffer];
	// 描画コンテキストを解放
	if([EAGLContext currentContext] == context)
	{
		[EAGLContext setCurrentContext:nil];
	}
	[context release];
	// 自身を解放
	[super dealloc];
}

// You must implement this method
+(Class)layerClass
{
    return [CAEAGLLayer class];
}

-(void)layoutSubviews
{
	[EAGLContext setCurrentContext:context];
	[self destroyFramebuffer];
	[self createFramebuffer];
}

-(BOOL)createFramebuffer
{
	GLint backingWidth;
	GLint backingHeight;

	glGenFramebuffersOES(1, &viewFramebuffer);
	glGenRenderbuffersOES(1, &viewRenderbuffer);

	glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
	glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
	[context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer];
	glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);

	glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
	glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);

	glGenRenderbuffersOES(1, &depthRenderbuffer);
	glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
	glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
	glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);

	if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES)
	{
		NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
		return NO;
	}

	Graph2D::GraphicDevice::initialize(
	   Graph2D::Vector2(backingWidth, backingHeight),
		//self.contentScaleFactor
	   Graph2D::Vector2(screenSize.width, screenSize.height)
	);

	return YES;
}

-(void)destroyFramebuffer
{
	Graph2D::GraphicDevice::finalize();

	glDeleteFramebuffersOES(1, &viewFramebuffer);
	viewFramebuffer = 0;
	glDeleteRenderbuffersOES(1, &viewRenderbuffer);
	viewRenderbuffer = 0;
	glDeleteRenderbuffersOES(1, &depthRenderbuffer);
	depthRenderbuffer = 0;
}

-(void)attach
{
	[self layoutSubviews];
	[self startAnimation];
}

-(void)dettach
{
	[self stopAnimation];
	[self removeFromSuperview];
}

////////////////////////////////////////////////////////////////////////////////
// draw
////////////////////////////////////////////////////////////////////////////////
#if defined(GRAPH2D_DESKTOP_VIEW_USE_DISPLAY_LINK)
-(void)drawView:(CADisplayLink*)displayLink
{
	if(animationRunning)
	{
		[EAGLContext setCurrentContext:context];

		glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
		glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);

		if(displayLink != nil)
		{
			float elapsedSeconds = displayLink.timestamp - m_timestamp;
			m_timestamp = displayLink.timestamp;

			Graph2D::Desktop::getInstance().updateAndDraw(elapsedSeconds);
		}

		[context presentRenderbuffer:GL_RENDERBUFFER_OES];
	}
}
#else
-(void)drawView
{
	if(animationRunning)
	{
		[EAGLContext setCurrentContext:context];

		glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
		glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);

//TODO:可変フレームレートに対応して下さい
		Graph2D::Desktop::getInstance().updateAndDraw(1.0f / 30.0f);

		[context presentRenderbuffer:GL_RENDERBUFFER_OES];
	}
}
#endif

////////////////////////////////////////////////////////////////////////////////
// animation
////////////////////////////////////////////////////////////////////////////////
-(void)initializeAnimation
{
#if defined(GRAPH2D_DESKTOP_VIEW_USE_DISPLAY_LINK)
	animationTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(drawView:)];
	[animationTimer setFrameInterval:animationInterval];
	[animationTimer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
#else
	animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES];
#endif
}

-(void)finalizeAnimation
{
	if(animationTimer)
	{
		[animationTimer invalidate];
		animationTimer = nil;
	}
}

-(void)startAnimation
{
	animationRunning = true;
}

-(void)stopAnimation
{
	animationRunning = false;
}

#if defined(GRAPH2D_DESKTOP_VIEW_USE_DISPLAY_LINK)
-(void)setAnimationInterval:(NSInteger)interval
{
	animationInterval = interval;
	[self finalizeAnimation];
	[self initializeAnimation];

//	PROFILE_SET_BASE_FPS(60.0 / (interval / (1.0 / 60.0)) + 0.5);
}
#else
- (void)setAnimationInterval:(NSTimeInterval)interval
{
	animationInterval = interval;
	[self finalizeAnimation];
	[self initializeAnimation];

	PROFILE_SET_BASE_FPS(60.0 / (interval / (1.0 / 60.0)) + 0.5);
}
#endif

////////////////////////////////////////////////////////////////////////////////
// touch
////////////////////////////////////////////////////////////////////////////////
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
	UITouch* touch = [[event allTouches] anyObject];
	CGPoint location = [touch locationInView:touch.view];
	Graph2D::Desktop::getInstance().touchesBegan(Graph2D::Vector2(location.x, location.y) * self.contentScaleFactor);
}

-(void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
	UITouch* touch = [[event allTouches] anyObject];
	CGPoint location = [touch locationInView:touch.view];
	Graph2D::Desktop::getInstance().touchesMoved(Graph2D::Vector2(location.x, location.y) * self.contentScaleFactor);
}

-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
	UITouch* touch = [[event allTouches] anyObject];
	CGPoint location = [touch locationInView:touch.view];
	Graph2D::Desktop::getInstance().touchesEnded(Graph2D::Vector2(location.x, location.y) * self.contentScaleFactor);
}

-(void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event
{
	UITouch* touch = [[event allTouches] anyObject];
	CGPoint location = [touch locationInView:touch.view];
	Graph2D::Desktop::getInstance().touchesCancelled(Graph2D::Vector2(location.x, location.y) * self.contentScaleFactor);
}

@end
